/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// nx::alloc
#include "memory/memalloc.h"
// nx::by_fibonacci
#include "algorithm/iterate.h"
// nx_assert
#include "bugfix/assert.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

/*
    Memory Expand Models
*/

template
<
    typename Alloc_, 
    size_t InitRemain_, 
    template <typename, size_t> class Model_
>
struct MemExpandNoReturn
{
    size_t block_size_;
    nx::Iterate<Model_<size_t, InitRemain_> > count_ite_;

    explicit MemExpandNoReturn(size_t block_size)
        : block_size_(block_size)
    {}
    
    size_t block_size(void) { return block_size_; }

    pvoid _(size_t& count)
    {
        nx_assert_static(InitRemain_);
        nx_assert(block_size_);
        count = *(++count_ite_);
        return nx::alloc<Alloc_, void>(block_size_ * count);
    }

    /* Never return memory back to system */
};

template
<
    typename Alloc_, 
    size_t InitRemain_, 
    template <typename, size_t> class Model_
>
struct MemExpandReturn : MemExpandNoReturn<Alloc_, InitRemain_, Model_>
{
    typedef MemExpandNoReturn<Alloc_, InitRemain_, Model_> base_t;

    struct blocks_t
    {
        pvoid     block_;
        blocks_t* pnext_;
    } * phead_, * ptail_;

    explicit MemExpandReturn(size_t block_size)
        : base_t(block_size)
        , phead_(NX_NULL)
        , ptail_(NX_NULL) {}

    pvoid _(size_t& count)
    {
        blocks_t* block = (blocks_t*)nx::alloc<Alloc_, void>(sizeof(blocks_t));
        block->block_ = base_t::_(count);
        block->pnext_ = NX_NULL;
        if (ptail_) ptail_->pnext_ = block;
        ptail_ = block;
        if(!phead_) phead_ = ptail_;
        return (block->block_);
    }

    virtual ~MemExpandReturn()
    {
        while(phead_)
        {
            blocks_t* block = phead_;
            phead_ = phead_->pnext_;
            nx::free<Alloc_>(block->block_, base_t::block_size_);
            nx::free<Alloc_>(block);
        }
    }
};

/*
    Default allocation parameters
*/

#ifndef NX_FIXEDPOOL_MODEL
#define NX_FIXEDPOOL_MODEL nx::by_fibonacci
#endif

#ifndef NX_FIXEDPOOL_INIT
#define NX_FIXEDPOOL_INIT  32
#endif

/*
    Fixed Memory Pool
*/

template
<
    typename Alloc_ = NX_ALLOC, 

    template
    <
        typename, 
        size_t, 
        template <typename, size_t> class
    >
    class Expand_ = MemExpandReturn,        /* Memory growth model uses the MemExpandReturn */

    template<typename, size_t>
    class Model_ = NX_FIXEDPOOL_MODEL,      /* Iterative algorithm using NX_FIXEDPOOL_MODEL */

    size_t InitRemain_ = NX_FIXEDPOOL_INIT  /* First initialize number of memory blocks */
>
class FixedPool : Expand_<Alloc_, InitRemain_, Model_>
{
public:
    typedef Expand_<Alloc_, InitRemain_, Model_> Expand;

protected:
    pvoid cursor_;

    void expand()
    {
        size_t count = 0;
        cursor_ = Expand::_(count);
        pvoid* p = (pvoid*)cursor_;
        count -= 1;
        for(size_t i = 0; i < count; ++i)
        {
            p = (pvoid*)( (*p) = ((byte*)p) + Expand::block_size() );
        }
        (*p) = NX_NULL;
    }

public:
    explicit FixedPool(size_t size)
        : Expand(nx_max(size, sizeof(cursor_)))
        , cursor_(NX_NULL)
    {}

public:
    pvoid alloc(void)
    {
        if (!cursor_) expand();
        pvoid p = cursor_;
        cursor_ = *(pvoid*)p;   // Get next block
        return p;
    }

    void free(pvoid p)
    {
        *(pvoid*)p = cursor_;
        cursor_ = p;            // Save free block
    }
};

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
